<?
/*-- Project Introduce --*/


/**
 * 代表补丁中一个发生变动的文件
 *
 * @author		alee
 * @access		public
 */
class JCAT_PatchFile
{
	/**
	 * 构造函数
	 *
	 * @access	public
	 * @return	void
	 */
	public function JCAT_PatchFile($sFilePath)
	{
		$this->sFilePath = $sFilePath ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$sPatchContent		string
	 * @return	void
	 */
	public function ParseModifies($sPatchContent)
	{
		$aRegexp = new JCAT_Preg(self::$sModifyRegexp) ;
		if( !$aRegexp->Match($sPatchContent) )
		{
			return ;
		}
		
		$aIterator = $aRegexp->CreateResultIter() ;
		for ( $aIterator->Last(); !$aIterator->IsDone(); $aIterator->Previous() )
		{
			// 修改处位置
			$nSourceLineStart = $aIterator->Current(self::$nModifyRegexpGrpSourceLineStart) ;
			$nSourceLineCount = $aIterator->Current(self::$nModifyRegexpGrpSourceLineCount) ;
			
			// 修改内容
			$nModifyPos = $aIterator->CurrentPos() ;
			$nContentPos = $nModifyPos + strlen($aIterator->Current()) ;
			
			$sModify = substr($sPatchContent,$nContentPos) ;
			$sPatchContent = substr_replace($sPatchContent,'',$nModifyPos) ;
			
			// 创建 Modify 对象
			$aModify = new JCAT_PatchModify($nSourceLineStart,$nSourceLineCount) ;
			$aModify->ParseModify($sModify) ;
			
			$this->PutInModify($aModify) ;
		}
	}
	
	/**
	 * 检查是否拥有存取权限
	 *
	 * @access	public
	 * @return	bool
	 */
	public function TestAccessMode($sApplyRootPath)
	{
		$sFilePath = $sApplyRootPath . '/' . $this->GetFilePath() ;
		
		// 文件已经存在，检查文件的存取权限
		if( is_file($sFilePath) )
		{
			return is_writable($sFilePath) ;
		}
		
		// 文件不存在，检查文件夹是否可创建文件
		else
		{
			$sPath = $sFilePath ;
			
			// 向上级递归到一个可创建的文件夹
			while( $sPath!='/' or !preg_match('/^[a-zA-Z]:$/',$sPath) )
			{
				$sPath = dirname($sPath) ;
				
				// 同名文件
				if( is_file($sPath) )
				{
					return false ;
				}
				
				// 目录存在
				else if( is_dir($sPath) )
				{
					return is_writable($sPath) ;
				}
				
				// 目录不存在
				else 
				{
					continue ;
				}
			}
			
			// 异常!
			return false ;
		}
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @param	$Parameter
	 * @return	void
	 */
	public function Apply($sApplyRootPath)
	{
		$sFilePath = $sApplyRootPath . '/' . $this->GetFilePath() ;
		
		// 删除文件
		if($this->isdeleting())
		{
			unlink($sFilePath) ;
			if( is_file($sFilePath) )
			{
				$sFilePath = JCAT_Global::TidyPath($sFilePath) ;
				throw new JCAT_Exception(JCAT_Language::SentenceEx(
					'无法删除文件：%s', 'JCAT', null, $sFilePath
				)) ;
			}
		}

		// 变更文件内容
		else
		{
			$this->CreateFileIfNotExists($sFilePath) ;
			
			// 将源文件内容载入到一个数组
			$sSource = file_get_contents($sFilePath) ;
			if($sSource)
			{
				$arrRequest = array() ;
				preg_match_all("/([^\\n]*)(\\n|$)/",$sSource,$arrRequest) ;
				$arrSourceLines = $arrRequest[0] ;
			}
			
			else
			{
				$arrSourceLines = array('') ;
			}
			
			// 应用所有 modify
			foreach ($this->arrModifies as $aModify)
			{
				$aModify->Apply($arrSourceLines) ;
			}
			
			// 保存修改过的内容
			file_put_contents( $sFilePath, implode('',$arrSourceLines) ) ;
		}
	}
	
	
	/**
	 * Description
	 *
	 * @access	private
	 * @param	$Parameter
	 * @return	void
	 */
	private function CreateFileIfNotExists($sFilePath)
	{
		// 新建文件
		if( is_file($sFilePath) )
		{
			return ;
		}
		
		$sDirPath = dirname($sFilePath) ;
		if(!is_dir($sDirPath))
		{
			mkdir($sDirPath,0777,true) ;
			chmod($sDirPath,0777) ;
			
			if( !is_writable($sDirPath) )
			{
				$sFilePath = JCAT_Global::TidyPath($sFilePath) ;
				throw new JCAT_Exception(JCAT_Language::SentenceEx(
					'无法以可写入权限创建文件夹: %s', 'JCAT', null, $sFilePath
				)) ;
			}
		}
		
		$hFile = fopen($sFilePath,'x') ;
		if( $hFile===false )
		{
			$sFilePath = JCAT_Global::TidyPath($sFilePath) ;
			throw new JCAT_Exception(JCAT_Language::SentenceEx(
				'无法创建文件: %s', 'JCAT', null, $sFilePath
			)) ;
		}
		
		fclose($hFile) ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	string
	 */
	public function GetFilePath()
	{
		return $this->sFilePath ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	void
	 */
	public function PutInModify(JCAT_PatchModify $aModify)
	{
		// 使用 修改处 行号做为键值，以便按位置排序
		$this->arrModifies[ $aModify->GetSourceLine() ] = $aModify ;
	}
	
	/**
	 * 设置对象属性 self::$bDeleting
	 *
	 * @access	public
	 * @param	$bDeleting		bool
	 * @return	void
	 */
	public function SetDeleting($bDeleting=true)
	{
		$this->bDeleting = $bDeleting? true: false ;
	}
	
	/**
	 * 取得对象属性 self::$bDeleting
	 *
	 * @access	public
	 * @return	bool
	 */
	public function IsDeleting()
	{
		return $this->bDeleting ;
	}
	
	/**
	 * Description
	 *
	 * @access	public
	 * @return	void
	 */
	public function IsCreating($sApplyRoot)
	{
		return !is_file($sApplyRoot.'/'.$this->GetFilePath()) ;
	}
	
	/**
	 * Description
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrModifies = array() ;
	
	/**
	 * 对象属性 PropertyDescription
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bDeleting ;
	
	
	static private $sModifyRegexp = "/@@ \\-(\\d+)(\\,(\\d+))? \\+(\\d+)(\\,(\\d+))? @@(\\n|$)/" ;
	static private $nModifyRegexpGrpSourceLineStart = 1 ;
	static private $nModifyRegexpGrpSourceLineCount = 3 ;
}

?>